package com.net.xpay.web.controller.merchant.auto;


import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.net.xpay.web.vo.ExcelErrDataVo;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.model.ResultData;
import com.net.common.model.ResultPagedList;
import com.net.common.util.PhoneUtil;
import com.net.common.util.SerializationUtil;
import com.net.common.util.StreamUtil;
import com.net.xpay.alipay.enums.IdentityType;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.core.CoreProperties;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.TransBizTypeConstant;
import com.net.xpay.core.domain.PayTrans;
import com.net.xpay.core.domain.auto.AutoPayBatch;
import com.net.xpay.core.domain.auto.AutoPayOrder;
import com.net.xpay.core.enums.AutoPayBatchStatus;
import com.net.xpay.core.enums.AutoPayOrderStatus;
import com.net.xpay.core.helper.UploadHelper;
import com.net.xpay.core.manager.auto.AutoPayBatchManager;
import com.net.xpay.core.manager.auto.AutoPayOrderManager;
import com.net.xpay.core.model.AlipayAuthModel;
import com.net.xpay.core.model.AutoPayBatchSearchModel;
import com.net.xpay.core.model.ResultModel;
import com.net.xpay.core.service.AlipayLogonIdAuthService;
import com.net.xpay.core.service.auto.AutoPayService;
import com.net.xpay.core.utils.EmailUtil;
import com.net.xpay.core.utils.ExcelImportUtil;
import com.net.xpay.core.utils.UuidUtil;
import com.net.xpay.web.secutiry.RequestUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

@RestController
@Slf4j
@RequestMapping("/merchant/auto-pay-batch")
@Api(tags = "商户/自动打款", description = "商户 自动打款 net")
public class AutoPayBatchController {


    @Autowired
    private UploadHelper uploadHelper;

    @Autowired
    private AutoPayBatchManager autoPayBatchManager;

    @Autowired
    private AutoPayOrderManager autoPayOrderManager;

    @Autowired
    private AutoPayService autoPayService;

    @Autowired
    private CoreProperties coreProperties;

    @Autowired
    private UserManager userManager;

    @Autowired
    private AlipayLogonIdAuthService alipayLogonIdAuthService;

    /**
     * 获取打款链接
     */
    @ApiOperation("获取打款链接")
    @GetMapping("/get-trans-url")
    public ResultData<String> getTransUrl(@ApiIgnore @RequestUser User merchant) {
        if (Strings.isNullOrEmpty(merchant.getUid())) {
            merchant.setUid(UuidUtil.uuid());
            userManager.updateByPrimaryKeyWithVersion(merchant);
        }
        String url = "http://" + coreProperties.getMobileDomain() + "/#/autoPayTrans?uid=" + merchant.getUid();
        return new ResultData<>(url);
    }

    /**
     * 搜索
     */
    @ApiOperation("搜索")
    @PostMapping("/search")
    public ResultPagedList<AutoPayBatch> searchAutoPayBath(@ApiIgnore @RequestUser User merchant, @RequestBody AutoPayBatchSearchModel searchModel) {
        Preconditions.checkArgument(searchModel != null);
        searchModel.setUserId(merchant.getId());
        List<AutoPayBatch> records = autoPayBatchManager.search(searchModel);
        long count = autoPayBatchManager.searchCount(searchModel);
        return new ResultPagedList<AutoPayBatch>(records,count, searchModel);
    }

    /**
     * 确定打款批次
     */
    @ApiOperation("确定打款批次")
    @PostMapping("/ensure")
    public ResultData<AutoPayBatch> ensureAutoPayBath(@ApiIgnore @RequestUser User merchant, Long id) {
        Preconditions.checkArgument(id != null);
        return new ResultData<>(autoPayService.ensureAutoPayBatch(merchant,id));
    }

    /**
     * 上传自动打款的表格
     */
    @ApiOperation("上传自动打款的表格")
    @PostMapping("/upload-excel")
    @WriteTransactional
    public ResultData<AutoPayBatch> uploadExcel(@RequestParam(value = "file", required = false) MultipartFile file, @ApiIgnore @RequestUser User merchant) {
        Preconditions.checkArgument(file != null);
        String fileName = file.getOriginalFilename();
        Preconditions.checkArgument(!Strings.isNullOrEmpty(fileName));
        if (!(fileName.endsWith("xls") || fileName.endsWith("xlsx"))) {
            log.warn("importPayTrans file suffix error：" + fileName);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "上传的文件格式错误|" + fileName);
        }
        long size = file.getSize();

        if (size > 10 * 1024 * 1024L) {
            log.warn("importPayTrans|file is too large|fileSize={}|userId={}", size, merchant.getId());
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "上传文件最大10M");
        }

        try {
            Workbook workBook = ExcelImportUtil.getWorkbook(file.getOriginalFilename(), file.getInputStream());
            Sheet sheet = workBook.getSheetAt(0);
            ResultModel<List<String>> resultModel = ExcelImportUtil.importListFromExcelUtilNull(workBook, sheet, 1, null);
            log.info("uploadExcel resultModel={}", SerializationUtil.obj2String(resultModel));
            if (!resultModel.isSuccess()) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, resultModel.getResultMessage());
            }

            String batchId = getBatchId(sheet, workBook);
            if (Strings.isNullOrEmpty(batchId)) {
                batchId = TransBizTypeConstant.AUTO + System.currentTimeMillis();
            }

            List<AutoPayOrder> orders = checkImportData(batchId, resultModel.getData(), merchant);

            checkAutoPayOrder(merchant, orders);
            //保存导入记录
            return new ResultData<>(savePayTrans(merchant, batchId, orders, file));
        } catch (IOException e) {
            log.info("importPayTrans error", e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统错误 请联系客服");
        }
    }

    private void checkAutoPayOrder(User merchant, List<AutoPayOrder> orders) {
        if (orders.size() > 1000L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "一次最多上传1000条");
        }
        List<AlipayAuthModel> accounts = new ArrayList<>();
        for (AutoPayOrder autoPayOrder : orders) {
            AlipayAuthModel model = new AlipayAuthModel();
            model.setIdentity(autoPayOrder.getIdentity());
            model.setIdentityType(autoPayOrder.getIdentityType());
            model.setName(autoPayOrder.getName());
            accounts.add(model);
        }
        List<PayTrans> payTrans = alipayLogonIdAuthService.authAlipayLogonId(merchant, merchant.getAlipayNickName(), accounts);
        Map<String, PayTrans> payTransMap = new HashMap<>();
        for (PayTrans trans : payTrans) {
            payTransMap.put(trans.getIdentity(), trans);
        }

        List<ExcelErrDataVo> errDataVoList = new ArrayList<>();
        for (int row = 0; row < orders.size(); row++) {
            AutoPayOrder autoPayOrder = orders.get(row);
            PayTrans trans = payTransMap.get(autoPayOrder.getIdentity());
            if (!Strings.isNullOrEmpty(trans.getErrorMsg())) {
                ExcelErrDataVo excelErrDataVo = new ExcelErrDataVo();
                excelErrDataVo.setRow(row + 3);
                excelErrDataVo.setAlipayNo(autoPayOrder.getIdentity());
                excelErrDataVo.setName(autoPayOrder.getName());
                excelErrDataVo.setMoney(autoPayOrder.getTransAmount().toString());
                excelErrDataVo.setImportBizNo(autoPayOrder.getImportBizNo());
                excelErrDataVo.setRemark(autoPayOrder.getRemark());
                excelErrDataVo.setErrMsg(trans.getErrorMsg());
                errDataVoList.add(excelErrDataVo);
            }
        }

        if (CollectionUtils.isNotEmpty(errDataVoList)) {
            // 将errDataVoList转为string
            String err = SerializationUtil.obj2String(errDataVoList);
            throw new BusinessException(PoseidonErrorCode.EXCEL_DATA_ILLEGAL, err);
        }
    }

    private AutoPayBatch savePayTrans(User merchant, String batchId, List<AutoPayOrder> orders, MultipartFile file) throws IOException {
        //先将文件保存起来 7天有效期
        LocalDateTime expired = LocalDateTime.now().plusDays(7L);
        String fileUrl = uploadHelper.upload("(" + UUID.randomUUID().toString().replace("-", "") + ")" + file.getOriginalFilename(), file.getInputStream(), expired);

        AutoPayBatch instance = AutoPayBatch.getInstance();
        instance.setUserId(merchant.getId());
        instance.setPartnerId(merchant.getPartnerId());
        instance.setFatherPartnerId(merchant.getFatherPartnerId());
        instance.setImportBatchNo(batchId);
        instance.setFileUrl(fileUrl);
        instance.setFileExpired(expired);


        instance.setTotalTransAmount(StreamUtil.sum(orders, AutoPayOrder::getTransAmount));

        instance.setTotalCount((long) orders.size());
        instance.setRemark(null);
        instance.setStatus(AutoPayBatchStatus.IMPORTED);
        instance.setVersion(0L);

        autoPayBatchManager.save(instance);

        for (AutoPayOrder order : orders) {
            order.setAutoPayBatchId(instance.getId());
        }

        autoPayOrderManager.batchSave(orders);
        return instance;
    }

    private List<AutoPayOrder> checkImportData(String batchId, List<List<String>> datas, User merchant) {
        AutoPayBatch batch = autoPayBatchManager.findByImportBatchNo(merchant.getId(), batchId);
        if (batch != null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "该业务批次号已被使用：" + batchId);
        }

        List<AutoPayOrder> orders = Lists.newArrayList();
        List<String> importBizNos = Lists.newArrayList();
        StringBuilder errMessage = new StringBuilder();
        // excel错误行数据
        List<ExcelErrDataVo> errDataVoList = new ArrayList<>();
        //第一行是标题行
        for (int i = 1; i < datas.size(); i++) {
            List<String> row = datas.get(i);
            String alipayNo = row.get(0);
            String name = row.get(1);
            String money = row.get(2);
            String importBizNo = row.get(3);
            String remark = row.get(4);

            //一个批次订单编号不能重复
            if (!Strings.isNullOrEmpty(importBizNo)) {
                if (importBizNos.contains(importBizNo)) {
                    errMessage.append(" (第").append(i + 2).append("行:").append("订单编号重复) ");
                    fillErrExcelData(i, datas, errDataVoList, "订单编号重复");
                    continue;
                }
                importBizNos.add(importBizNo);
            }

            //买家昵称 和 支付宝账号&真实姓名 二选一必填一项
            if ((Strings.isNullOrEmpty(alipayNo) || Strings.isNullOrEmpty(name))) {
                errMessage.append(" (第").append(i + 2).append("行:").append("支付宝账号&真实姓名必填");
                fillErrExcelData(i, datas, errDataVoList, "支付宝账号&真实姓名必填");
                continue;
            }

            if (Strings.isNullOrEmpty(money)) {
                errMessage.append(" (第").append(i + 2).append("行:").append("打款金额不能为空) ");
                fillErrExcelData(i, datas, errDataVoList, "打款金额不能为空");
                continue;
            }

            AutoPayOrder instance = AutoPayOrder.getInstance();
            instance.setUserId(merchant.getId());
            instance.setPartnerId(merchant.getPartnerId());
            instance.setFatherPartnerId(merchant.getFatherPartnerId());
            instance.setAutoPayBatchId(null);
            instance.setImportBatchNo(batchId);
            instance.setImportBizNo(importBizNo);
            instance.setTransAmount(new BigDecimal(money));
            instance.setRemark(remark);
            instance.setName(name);
            instance.setStatus(AutoPayOrderStatus.IMPORTED);
            instance.setIsSelf(0);
            instance.setVersion(0L);

            //支付宝账号优先 可以转的金额更大
            if (!Strings.isNullOrEmpty(alipayNo) && !Strings.isNullOrEmpty(name)) {
                if (alipayNo.contains(" ")) {
                    errMessage.append(" (第").append(i + 2).append("行:").append("支付宝账号不能有空格) ");
                    fillErrExcelData(i, datas, errDataVoList, "支付宝账号不能有空格");
                    continue;
                }
                if (!alipayNo.contains("@")) {
                    if (!PhoneUtil.isEffectiveMobile(alipayNo)) {
                        errMessage.append(" (第").append(i + 2).append("行:").append("支付宝账号格式错误) ");
                        fillErrExcelData(i, datas, errDataVoList, "支付宝账号格式错误");
                        continue;
                    }
                } else {
                    if (!EmailUtil.checkEmail(alipayNo)) {
                        errMessage.append(" (第").append(i + 2).append("行:").append("支付宝账号格式错误) ");
                        fillErrExcelData(i, datas, errDataVoList, "支付宝账号格式错误");
                        continue;
                    }
                }

                instance.setIdentity(alipayNo);
                instance.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
            }
            checkIsDigit(i, money, instance, errMessage, datas, errDataVoList);

            orders.add(instance);
        }

        if (!Strings.isNullOrEmpty(errMessage.toString())) {
            // 将errDataVoList转为string
            String err = SerializationUtil.obj2String(errDataVoList);
            throw new BusinessException(PoseidonErrorCode.EXCEL_DATA_ILLEGAL, err);
        }

        if (CollectionUtils.isEmpty(orders)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "至少上传一条打款记录");
        }

        return orders;
    }

    private void checkIsDigit(int i, String money, AutoPayOrder instance, StringBuilder errMessage, List<List<String>> datas, List<ExcelErrDataVo> errDataVoList) {
        BigDecimal m;
        try {
            m = new BigDecimal(money);
        } catch (Exception e) {
            errMessage.append(" (第").append(i + 2).append("行:").append("打款金额格式不正确) ");
            fillErrExcelData(i, datas, errDataVoList, "打款金额格式不正确");
            return;
        }

        if (m.intValue() < 1) {
            errMessage.append(" (第").append(i + 2).append("行:").append("打款金额不能低于1元) ");
            fillErrExcelData(i, datas, errDataVoList, "打款金额不能低于1元");
            return;
        }

        if (IdentityType.TAOBAO_NICKNAME.getValue().equals(instance.getIdentityType())) {
            if (m.intValue() > 1000) {
                errMessage.append(" (第").append(i + 2).append("行:").append("收款账户类型是淘宝昵称的打款金额不能大于1000元) ");
                fillErrExcelData(i, datas, errDataVoList, "收款账户类型是淘宝昵称的打款金额不能大于1000元");
            }
        } else {
            if (m.intValue() > 50000) {
                errMessage.append(" (第").append(i + 2).append("行:").append("打款金额不能大于50000元) ");
                fillErrExcelData(i, datas, errDataVoList, "打款金额不能大于50000元");
            }
        }
    }

    private String getBatchId(Sheet sheet, Workbook workBook) {
        ResultModel<List<String>> resultModel = ExcelImportUtil.importListFromExcelUtilNull(workBook, sheet, 0, 0);
        String batchId;
        try {
            batchId = resultModel.getData().get(0).get(5);
        } catch (Exception e) {
            return null;
        }

        return batchId;
    }

    /**
     * @param row   : 行数
     * @param datas : excel总数据
     * @Description:[将excel中错误的数据封装到ExcelErrDataVo对象]

     * @date 2020/11/3 11:46
     */
    private void fillErrExcelData(Integer row, List<List<String>> datas, List<ExcelErrDataVo> errDataVoList, String errMsg) {
        ExcelErrDataVo excelErrDataVo = new ExcelErrDataVo();
        // 获取list集合中指定索引的数据
        List<String> list = datas.get(row);
        // 封装excelErrDataVo对象
        excelErrDataVo.setRow(row + 2);
        excelErrDataVo.setAlipayNo(list.get(0));
        excelErrDataVo.setName(list.get(1));
        excelErrDataVo.setMoney(list.get(2));
        excelErrDataVo.setImportBizNo(list.get(3));
        excelErrDataVo.setRemark(list.get(4));
        excelErrDataVo.setErrMsg(errMsg);
        errDataVoList.add(excelErrDataVo);
    }
}
